#include "Windows/GxWinForm.h"
#include "Windows/GxWinApp.h"
#include "LibGL/GxUI.h"
#include "Windows/GxWinCommonDlg.h"
#include "LibGL/GxTextures.h"
#include "Basic/FileSystem.h"

using namespace GxWinGUI;
using namespace GxRendering::UI;
using namespace GxRendering;

using namespace GxLibBasic;

class TexConvForm : public GLForm
{
private:
	Timer tmrUI;
	RefPtr<UIEntry> ui;
	RefPtr<FileDialog> dlgOpen;
	GxRendering::UI::TextBox * txtMain, * txtAlpha;
	GxRendering::UI::RadioBox * rdoUseAlphaMap, * rdoUseAlphaFromSource, *rdoNoAlpha;
	GxRendering::UI::CheckBox * chkNormal;
	String fileName;
protected:
	bool ProcessMessage(GxWinGUI::WinMessage msg)
	{
		BeginGLDrawing();
		Global::MakeCurrent(ui.operator ->());
		bool rs = GLForm::ProcessMessage(msg);
		ui->HandleSystemMessage(msg.hWnd, msg.message, msg.wParam, msg.lParam);
		return rs;
	}
public:
	TexConvForm()
	{
		dlgOpen = new FileDialog(this);
		dlgOpen->DefaultEXT = L"bmp";
		dlgOpen->FileMustExist = true;
		dlgOpen->Filter = L"Bitmaps|*.bmp|All Files|*.*";
		Init();
	}

	~TexConvForm()
	{
		Finalize();
	}

	void Init()
	{
		int w = SystemMetrics::GetScreenWidth();
		int h = SystemMetrics::GetScreenHeight();

		tmrUI.Interval = 50;
		tmrUI.OnTick.Bind(this, &TexConvForm::UITimerTick);
		tmrUI.StartTimer();
		Global::Init();
		ui = new UIEntry(GetClientWidth(), GetClientHeight());
		SetClientWidth(350);
		SetClientHeight(150);
		SetBorder(fbFixedDialog);
		SetText(L"Texture Converter");
		SetMaximizeBox(false);

		SetLeft((w-GetWidth())/2);
		SetTop((h-GetHeight())/2);

		GxRendering::UI::Label * lbl;
		GxRendering::UI::Button * btn;

		lbl = new GxRendering::UI::Label();
		lbl->SetText(L"Main Source:");
		lbl->Posit(20,20, 80,20);
		ui->AddChild(lbl);
		
		txtMain = new GxRendering::UI::TextBox();
		txtMain->Posit(110,18, 200,20); 
		ui->AddChild(txtMain);
		
		btn = new GxRendering::UI::Button();
		btn->SetText(L"...");
		btn->Posit(310,16,20,20);
		btn->OnClick.Bind(this, &TexConvForm::btnBrowseMainClick);
		ui->AddChild(btn);


		lbl = new GxRendering::UI::Label();
		lbl->SetText(L"Alpha Source:");
		lbl->Posit(20,50, 80,20);
		ui->AddChild(lbl);
		
		txtAlpha = new GxRendering::UI::TextBox();
		txtAlpha->Posit(110,46, 200,20); 
		ui->AddChild(txtAlpha);
		
		btn = new GxRendering::UI::Button();
		btn->SetText(L"...");
		btn->OnClick.Bind(this, &TexConvForm::btnBrowseAlphaClick);
		btn->Posit(310,48,20,20);
		ui->AddChild(btn);

		lbl = new GxRendering::UI::Label();
		lbl->SetText(L"Alpha:");
		lbl->Posit(20,80, 80,20);
		ui->AddChild(lbl);

		rdoNoAlpha = new GxRendering::UI::RadioBox();
		rdoNoAlpha->SetText(L"None");
		rdoNoAlpha->Posit(90, 80, 45, 20);
		rdoNoAlpha->BackColor = ui->BackColor;
		rdoNoAlpha->Checked = true;
		ui->AddChild(rdoNoAlpha);

		rdoUseAlphaFromSource = new GxRendering::UI::RadioBox();
		rdoUseAlphaFromSource->SetText(L"From Source");
		rdoUseAlphaFromSource->Posit(150, 80, 90, 20);
		rdoUseAlphaFromSource->BackColor = ui->BackColor;
		ui->AddChild(rdoUseAlphaFromSource);

		rdoUseAlphaMap = new GxRendering::UI::RadioBox();
		rdoUseAlphaMap->SetText(L"From Alpha Map");
		rdoUseAlphaMap->Posit(245, 80, 80, 20);
		rdoUseAlphaMap->BackColor = ui->BackColor;
		ui->AddChild(rdoUseAlphaMap);

		chkNormal = new GxRendering::UI::CheckBox();
		chkNormal->SetText(L"Normal Map");
		chkNormal->Posit(25, 100, 80, 20);
		chkNormal->BackColor = ui->BackColor;
		ui->AddChild(chkNormal);

		btn = new GxRendering::UI::Button();
		btn->SetText(L"Convert");
		btn->Posit(160,110,80,28);
		btn->OnClick.Bind(this, &TexConvForm::btnConvertClick);
		ui->AddChild(btn);

		btn = new GxRendering::UI::Button();
		btn->SetText(L"Close");
		btn->Posit(250,110,80,28);
		btn->OnClick.Bind(this, &TexConvForm::btnCloseClick);
		ui->AddChild(btn);
	}

	void btnCloseClick(UI_Base * sender)
	{
		Close();
	}

	void btnBrowseMainClick(UI_Base * sender)
	{
		if (dlgOpen->ShowOpen())
		{
			txtMain->SetText(dlgOpen->FileName);
		}
	}

	void btnBrowseAlphaClick(UI_Base * sender)
	{
		if (dlgOpen->ShowOpen())
		{
			txtAlpha->SetText(dlgOpen->FileName);
		}
	}

	float clamp(float v, float min, float max)
	{
		if (v<min)
			return min;
		if (v>max)
			return max;
		return v;
	}

	void btnConvertClick(UI_Base * sender)
	{
		if (!FileExists(txtMain->GetText()))
		{
			MessageBox(L"RGB source bitmap does not exists.", L"Texture Converter", MB_ICONEXCLAMATION);
			return;
		}
		if (rdoUseAlphaMap->Checked && !FileExists(txtAlpha->GetText()))
		{
			MessageBox(L"Alpha source bitmap does not exists.", L"Texture Converter", MB_ICONEXCLAMATION);
			return;
		}
		try
		{
			bool useAlpha = !rdoNoAlpha->Checked;
			bool normal = chkNormal->Checked;
			GxWinGUI::Drawing::Bitmap s(txtMain->GetText());
			RefPtr<GxWinGUI::Drawing::Bitmap> alpha;
			if (rdoUseAlphaMap->Checked)
			{
				alpha = new GxWinGUI::Drawing::Bitmap(txtAlpha->GetText());
				if (alpha->GetWidth() != s.GetWidth() || alpha->GetHeight() != s.GetHeight())
				{
					throw 101;
				}
			}
			RefPtr<Texture> tex = new Texture();
			tex->Width = s.GetWidth();
			tex->Height = s.GetHeight();
			tex->Channels = useAlpha?4:3;
			tex->DataType = Texture::Char;
			tex->Pixels = new unsigned char[tex->Width * tex->Height * tex->Channels];
			for (int i=0; i<tex->Height; i++)
			{
				unsigned char * line = s.ScanLine[i];
				unsigned char * linea = 0;
				if (rdoUseAlphaMap->Checked)
					linea = alpha->ScanLine[i];
				for (int j=0; j<tex->Width; j++)
				{
					if (!normal)
					{
						tex->Pixels[(i*tex->Width+j)*tex->Channels] = line[j*4+2];
						tex->Pixels[(i*tex->Width+j)*tex->Channels+1] = line[j*4+1];
						tex->Pixels[(i*tex->Width+j)*tex->Channels+2] = line[j*4];
						if (useAlpha)
						{
							if (rdoUseAlphaFromSource->Checked)
							{
								tex->Pixels[(i*tex->Width+j)*tex->Channels+3] = line[j*4+3];
							}
							else
							{
								tex->Pixels[(i*tex->Width+j)*tex->Channels+3] = (linea[j*4]+linea[j*4+1]+linea[j*4+2])/3;
							}
						}
					}
					else
					{
						vec3 v;
						v.x = line[j*4+2]/ 255.0f;
						v.y = line[j*4+1]/ 255.0f;
						v.z = line[j*4] / 255.0f;
						v *= 2.0f;
						v -= vec3(1.0f,1.0f,1.0f);
						vec3 base1(-0.40824f,  0.707107f,  0.57735f);
						vec3 base2(-0.40824f,  -0.707107f,  0.57735f);
						vec3 base3(0.81650f, 0.0f, 0.57735f);
						vec3 rs;
						rs.x = clamp(v * base1,0.0f,1.0f);
						rs.y = clamp(v * base2,0.0f,1.0f);
						rs.z = clamp(v * base3,0.0f,1.0f);
						rs *= rs;
						float sum = rs.x + rs.y + rs.z;
						rs *= 1.0f/sum;
						tex->Pixels[(i*tex->Width+j)*tex->Channels] = (unsigned char)(rs.x * 255.0f);
						tex->Pixels[(i*tex->Width+j)*tex->Channels+1] = (unsigned char)(rs.y * 255.0f);
						tex->Pixels[(i*tex->Width+j)*tex->Channels+2] = (unsigned char)(rs.z * 255.0f);
					}
				}
			}
			String filename =	ExtractFilePath(txtMain->GetText()) + ExtractFileNameWithoutEXT(txtMain->GetText()) + L".gxt";
			tex->SaveToFile(filename);
			MessageBox(String(L"Texture converted to \"")+filename + L"\"", L"Texture Converter", MB_ICONINFORMATION);
		}
		catch (int a)
		{
			if (a == 101)
			{
				MessageBox(L"Alpha map not consistant with source.", L"Texture Converter", MB_ICONEXCLAMATION);
			}
		}
		catch(...)
		{
			MessageBox(L"Illegal bitmap.", L"Texture Converter", MB_ICONEXCLAMATION);
		}
	}

	void Finalize()
	{
		Global::Finalize();
	}

	void UITimerTick(Object * sender, GxWinGUI::EventArgs e)
	{
		BeginGLDrawing();
		Global::MakeCurrent(ui.operator ->());
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		ui->DrawUI();
		SwapBuffers(wglGetCurrentDC());
	}


};

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
{
	Application::Init(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
	Application::Run(new TexConvForm());
	Application::Dispose();
	return 1;
}